home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Snippets / Devices / SCSI Simple Sample / Src / LogManager.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  33.5 KB  |  1,313 lines  |  [TEXT/KAHL]

  1. /*                                    LogManager.c                                */
  2. /*
  3.  * LogManager.c
  4.  * Copyright © 1993 Apple Computer Inc. All rights reserved.
  5.  *
  6.  * These functions manage a logging display for error messages and other text.
  7.  * The log is implemented as a ListManager list that can hold nLogItems. This
  8.  * module is intentionally more-or-less self-contained so it can easily be
  9.  * exported to other applications.
  10.  */
  11. #include "LogManager.h"
  12. #ifndef THINK_C                /* MPW includes            */
  13. #include <Errors.h>
  14. #include <Script.h>
  15. #include <Types.h>
  16. #include <Files.h>
  17. #include <Resources.h>
  18. #include <QuickDraw.h>
  19. #include <Fonts.h>
  20. #include <Events.h>
  21. #include <Windows.h>
  22. #include <ToolUtils.h>
  23. #include <Memory.h>
  24. #include <Menus.h>
  25. #include <Lists.h>
  26. #include <Printing.h>
  27. #include <Dialogs.h>
  28. #include <StandardFile.h>
  29. #endif
  30. #include <Serial.h>
  31. #include <Printing.h>
  32. #include <Packages.h>
  33. #pragma segment LogManager
  34.  
  35. /*
  36.  * There is a 32000 byte maximum for the list, so don't
  37.  * make nLogLines too big.
  38.  */
  39. #ifndef nDefaultLogLines
  40. #define nDefaultLogLines    128
  41. #endif
  42. #define width(r)                ((r).right - (r).left)
  43. #define height(r)                ((r).bottom - (r).top)
  44. #ifndef TRUE
  45. #define TRUE                    1
  46. #define FALSE                    0
  47. #endif
  48. enum {
  49.     kScrollBarWidth     = 16,
  50.     kScrollBarOffset    = kScrollBarWidth - 1,
  51.     kActiveControl        = 0,        /* Normal button hilite                */
  52.     kDisabledControl    = 255        /* Disabled button hilite            */
  53. };
  54. /*
  55.  * When the horizontal scrollbar is at zero, the list is indented +4 pixels.
  56.  * When the horizontal scrollbar increases, the list indent decreases. This
  57.  * value is "by inspection," but we could extract it from the list record
  58.  * when the list is initially created.
  59.  */
  60. enum {
  61.     kZeroIndent            = 4
  62. };
  63. /*
  64.  * This sets a nominal value for the maximum cell width. This might be
  65.  * better provided as a user-settable parameter.
  66.  */
  67. #define kMaxHorizontalScroll    (CharWidth('M') * 255)
  68. static const char        endOfLine[1] = { 0x0D };    /* <CR>            */
  69.  
  70. /*
  71.  * Cheap 'n dirty pascal string copy routine.
  72.  */
  73. #ifndef pstrcpy
  74. #define pstrcpy(dst, src) do {                            \
  75.         StringPtr    _src = (src);                        \
  76.         BlockMove(_src, dst, _src[0] + 1);                \
  77.     } while (0)
  78. #endif
  79. /*
  80.  * Cheap 'n dirty pascal string concat.
  81.  */
  82. #ifndef pstrcat
  83. #define pstrcat(dst, src) do {                            \
  84.         StringPtr        _dst = (dst);                    \
  85.         StringPtr        _src = (src);                    \
  86.         short            _len;                            \
  87.         _len = 255 - _dst[0];                            \
  88.         if (_len > _src[0]) _len = _src[0];                \
  89.         BlockMove(&_src[1], &_dst[1] + _dst[0], _len);    \
  90.         _dst[0] += _len;                                \
  91.     } while (0)
  92. #endif
  93.  
  94. static OSErr                        AddStringToList(
  95.         ListHandle                        logListHandle,
  96.         ConstStr255Param                theString
  97.     );
  98. static pascal void                    ScrollLogAction(
  99.         register ControlHandle            theControl,
  100.         short                            partcode
  101.     );
  102. static void                            ScrollLogList(
  103.         ControlHandle                    theControl
  104.     );
  105. static StringHandle                    GetLogStringHandle(
  106.         ListHandle                        logListHandle,
  107.         short                            theRow
  108.     );
  109.  
  110. #define LIST            (**logListHandle)
  111. #define LOGINFO            (**((LogInfoHdl) (LIST.userHandle)))
  112. #define HSCROLL            (LOGINFO.hScroll)
  113. #define IS_COLOR(port)    (((((CGrafPtr) (port))->portVersion) & 0xC000) != 0)
  114. #define COLOR_LIST        (IS_COLOR(LIST.port))
  115.  
  116. /*
  117.  * This code sequence is copied into the ListProc handle. It is designed so we
  118.  * don't have to flush the instruction and data caches. Note that this must
  119.  * be revised if you compile for a non-68000 environment.
  120.  */
  121. static const short gDummyLDEF[] = {
  122.         0x207A,                /*        movea.l    procPtr,a0            */
  123.         0x0004,                /*                <offset to procPtr>    */
  124.         0x4ED0,                /*        jmp        a0                     */
  125.         0x0000, 0x0000        /*        dc.l    <Drawing Proc here>    */
  126. };
  127.  
  128. static pascal void
  129. LogListDefProc(
  130.         short                    listMessage,
  131.         Boolean                    listSelect,
  132.         Rect                    *listRect,
  133.         Cell                    listCell,
  134.         short                    listDataOffset,
  135.         short                    listDataLen,
  136.         ListHandle                errorLogList
  137.     );
  138.  
  139. /*
  140.  * Create the data display list.
  141.  */
  142. ListHandle
  143. CreateLog(
  144.         const Rect                    *viewRect,
  145.         short                        listFontNumber,
  146.         short                        listFontSize,
  147.         short                        logLines
  148.     )
  149. {
  150.         OSErr                        status;
  151.         ListHandle                    logListHandle;
  152.         FontInfo                    info;
  153.         Point                        cellSize;
  154.         short                        listHeight;
  155.         Rect                        dataBounds;
  156.         Rect                        listRect;
  157.         short                        listFontHeight;
  158.         Handle                        drawProcHdl;
  159.         LogInfoRecord                logInfo;
  160.         Handle                        logInfoHdl;
  161.         ProcPtr                        listProcPtr;
  162.         short                        horizontalMax;
  163.         
  164.         logInfoHdl = NULL;
  165.         drawProcHdl = NULL;
  166.         if (logLines == 0)
  167.             logLines = nDefaultLogLines;
  168.         TextFont(listFontNumber);
  169.         TextSize(listFontSize);
  170.         GetFontInfo(&info);
  171.         listFontHeight = info.ascent + info.descent + info.leading;
  172.         /*
  173.          * Compute the list drawing area, adjusting the list
  174.          * area height so an integral number of lines will be drawn. As with
  175.          * the standard list manager, the scroll bars will be drawn outside
  176.          * of the list rectangle.
  177.          */
  178.         listRect = *viewRect;
  179.         /*
  180.          * Normalize the list area so that an integral number of cells are visible
  181.          * on the display and define the visual shape of each cell.
  182.          */
  183.         listHeight = height(listRect);
  184.         listHeight -= (listHeight % listFontHeight);
  185.         listRect.bottom = listRect.top + listHeight;
  186.         SetPt(&cellSize, width(listRect), listFontHeight);
  187.         /*
  188.          * Note: we create a one-column list with both vertical and horizontal
  189.          * scrollbars, then we steal the horizontal scrollbar because we're
  190.          * scrolling within the list cell. Unfortunately, the List Manager only
  191.          * scrolls from one cell (column in the horizontal direction) to another.
  192.          */
  193.         SetRect(&dataBounds, 0, 0, 1, 0);
  194.         logListHandle = LNew(
  195.                 &listRect,                            /* Viewing area                */
  196.                 &dataBounds,                        /* Rows and col's            */
  197.                 cellSize,                            /* Element size                */
  198.                 0,                                    /* No defProc yet            */
  199.                 qd.thePort,                            /* Display window            */
  200.                 TRUE,                                /* Draw it                    */
  201.                 FALSE,                                /* No grow box                */
  202.                 TRUE,                                /* Has horizontal scroll    */
  203.                 TRUE                                /* Has vertical scroll        */
  204.             );
  205.         if (logListHandle == NULL)
  206.             goto failure;
  207.         LIST.selFlags = lOnlyOne;
  208.         LIST.listFlags = lDoVAutoscroll;            /* Vertical autoscroll only    */
  209.         logInfo.logFileRefNum = 0;
  210.         logInfo.logFileVRefNum = 0;
  211.         logInfo.logFileStatus = 0;
  212.         logInfo.logLines = logLines;
  213.         logInfo.fontNumber = listFontNumber;
  214.         logInfo.fontSize = listFontSize;
  215.         if (COLOR_LIST) {
  216.             GetForeColor(&logInfo.foreColor);
  217.             GetBackColor(&logInfo.backColor);
  218.         }
  219.         status = PtrToHand(&logInfo, &logInfoHdl, sizeof logInfo);
  220.         if (status != noErr)
  221.             goto failure;
  222.         LIST.userHandle = (Handle) logInfoHdl;
  223.         HSCROLL = LIST.hScroll;                        /* Grab horizontal scroller    */
  224.         LIST.hScroll = NULL;                        /* Remove it from the list    */
  225.         SetCRefCon(HSCROLL, (long) logListHandle);    /* Link scrollbar to list    */
  226.         status = PtrToHand(gDummyLDEF, &drawProcHdl, sizeof gDummyLDEF);
  227.         if (status != noErr)
  228.             goto failure;
  229.         listProcPtr = (ProcPtr) LogListDefProc;
  230.         BlockMove(&listProcPtr, &((short *) *drawProcHdl)[3], sizeof listProcPtr);
  231.         LIST.listDefProc = drawProcHdl;
  232.         horizontalMax = kMaxHorizontalScroll - width((**HSCROLL).contrlRect);
  233.         if (horizontalMax < 0)
  234.             horizontalMax = 0;
  235.         SetCtlMin(HSCROLL, 0);
  236.         SetCtlMax(HSCROLL, horizontalMax);
  237.         SetCtlValue(HSCROLL, 0);
  238.         HiliteControl(
  239.             HSCROLL,
  240.             (horizontalMax == 0) ? kDisabledControl : kActiveControl
  241.         );
  242.         goto success;
  243. failure:
  244.         if (drawProcHdl != NULL) {
  245.             DisposeHandle((Handle) drawProcHdl);
  246.             LIST.listDefProc = NULL;
  247.         }
  248.         DisposeLog(logListHandle);
  249.         logListHandle = NULL;
  250. success:
  251.         return (logListHandle);
  252. }
  253.  
  254. /*
  255.  * DisposeLog disposes of our private information and then disposes of the list.
  256.  */
  257. void
  258. DisposeLog(
  259.         ListHandle                        logListHandle
  260.     )
  261. {
  262.         if (logListHandle != NULL) {
  263.             if (LIST.userHandle != NULL) {
  264.                 LIST.hScroll = HSCROLL;            /* The list manager disposes    */ 
  265.                 DisposeHandle(LIST.userHandle);
  266.                 LIST.userHandle = NULL;
  267.             }
  268.             LDispose(logListHandle);
  269.         }
  270. }
  271.  
  272. /*
  273.  * UpdateLogWindow explicitly updates the log's window. It is generally called only
  274.  * after Modal Dialogs or Alerts obscured the window.
  275.  */
  276. void
  277. UpdateLogWindow(
  278.         ListHandle                        logListHandle
  279.     )
  280. {
  281.         WindowPtr                        theWindow;
  282.         GrafPtr                            savePort;
  283.         Rect                            viewRect;
  284.         RgnHandle                        listRgn;
  285.         RgnHandle                        clipRgn;
  286.         
  287.         if (logListHandle != NULL) {
  288.             theWindow = (WindowPtr) LIST.port;
  289.             if (EmptyRgn(((WindowPeek) theWindow)->updateRgn) == FALSE) {
  290.                 viewRect = LIST.rView;
  291.                 if (LIST.hScroll != NULL)
  292.                     viewRect.bottom += kScrollBarWidth;
  293.                 if (LIST.hScroll != NULL)
  294.                     viewRect.right += kScrollBarWidth;
  295.                 listRgn = NewRgn();
  296.                 RectRgn(listRgn, &viewRect);
  297.                 SectRgn(listRgn, ((WindowPeek) theWindow)->updateRgn, listRgn);
  298.                 if (EmptyRgn(listRgn) == FALSE) {
  299.                     /*
  300.                      * We have something to redraw. Fake an update event handler.
  301.                      */
  302.                     GetPort(&savePort);
  303.                     SetPort(theWindow);
  304.                     clipRgn = NewRgn();
  305.                     GetClip(clipRgn);
  306.                     SetClip(listRgn);
  307.                     EraseRgn(listRgn);
  308.                     UpdateLog(logListHandle);
  309.                     SetClip(clipRgn);
  310.                     DisposeRgn(clipRgn);
  311.                     ValidRgn(listRgn);
  312.                     SetPort(savePort);
  313.                 }
  314.                 DisposeRgn(listRgn);
  315.             }
  316.         }
  317. }
  318.  
  319. /*
  320.  * UpdateLog redraws the list.
  321.  */
  322. void
  323. UpdateLog(
  324.         ListHandle                        logListHandle
  325.     )
  326. {
  327.         Rect                        viewRect;
  328.         RGBColor                    saveForeColor;
  329.         RGBColor                    saveBackColor;
  330.         
  331.         if (logListHandle != NULL) {
  332.             /*
  333.              * Make sure the list is locked down while we draw. Note that we
  334.              * assume that the application has called UpdateControls, so
  335.              * the horizontal scrollbar is correctly drawn.
  336.              */        
  337.             if (COLOR_LIST) {
  338.                 GetForeColor(&saveForeColor);
  339.                 GetBackColor(&saveBackColor);
  340.                 RGBForeColor(&LOGINFO.foreColor);
  341.                 RGBBackColor(&LOGINFO.backColor);
  342.             }
  343.             viewRect = LIST.rView;
  344.             /*
  345.              * Include the scrollbars in the frame.
  346.              */
  347.             EraseRect(&viewRect);
  348.             InsetRect(&viewRect, -1, -1);
  349.             viewRect.right += kScrollBarOffset;
  350.             viewRect.bottom += kScrollBarOffset;
  351.             FrameRect(&viewRect);
  352.             LUpdate(LIST.port->visRgn, logListHandle);
  353.             if (COLOR_LIST) {
  354.                 RGBForeColor(&saveForeColor);
  355.                 RGBBackColor(&saveBackColor);
  356.             }
  357.         }
  358. }
  359.  
  360. /*
  361.  * ActivateLog activates (or deactivates) the log. Call it on activate and
  362.  * suspendResume events.
  363.  */
  364. void
  365. ActivateLog(
  366.         ListHandle                        logListHandle,
  367.         Boolean                            activating
  368.     )
  369. {
  370.         if (logListHandle != NULL) {
  371.             LActivate(activating, logListHandle);
  372.             HiliteControl(
  373.                 HSCROLL,
  374.                 (activating) ? kActiveControl : kDisabledControl);
  375.         }
  376. }
  377.  
  378. /*
  379.  * MoveLog Repositions the log list area.
  380.  */
  381. void
  382. MoveLog(
  383.         ListHandle                        logListHandle,
  384.         short                            leftEdge,
  385.         short                            topEdge
  386.     )
  387. {
  388.         Rect                            viewRect;
  389.         
  390.         if (logListHandle != NULL) {
  391.             if (LIST.rView.left != leftEdge || LIST.rView.top != topEdge) {
  392.                 viewRect = LIST.rView;
  393.                 InsetRect(&viewRect, -1, -1);
  394.                 InvalRect(&viewRect);
  395.                 OffsetRect(
  396.                     &LIST.rView,
  397.                     leftEdge - LIST.rView.left,
  398.                     topEdge -LIST.rView.top
  399.                 );
  400.                 viewRect = LIST.rView;
  401.                 InsetRect(&viewRect, -1, -1);
  402.                 InvalRect(&viewRect);
  403.                 MoveControl(
  404.                     LIST.vScroll,
  405.                     LIST.rView.right - kScrollBarOffset,
  406.                     LIST.rView.top - 1
  407.                 );
  408.                 MoveControl(
  409.                     HSCROLL,
  410.                     LIST.rView.left - 1,
  411.                     LIST.rView.bottom - kScrollBarOffset
  412.                 );
  413.             }
  414.         }
  415. }
  416.  
  417. /*
  418.  * SizeLog: this is the list rectangle size and does not include the
  419.  * horizontal and vertical scrollbars.
  420.  */
  421. void
  422. SizeLog(
  423.         ListHandle                        logListHandle,
  424.         short                            newWidth,
  425.         short                            newHeight
  426.     )
  427. {
  428.         Rect                            viewRect;
  429.         Point                            cellSize;
  430.         short                            horizontalMax;
  431.         
  432.         if (logListHandle != NULL) {
  433.             viewRect = LIST.rView;
  434.             InsetRect(&viewRect, -1, -1);
  435.             InvalRect(&viewRect);
  436.             /*
  437.              * We put the horizontal scrollbar back into the list record so that
  438.              * the list manager kindly resizes it. Then we take it out again.
  439.              */
  440.             LIST.hScroll = HSCROLL;
  441.             LSize(newWidth, newHeight, logListHandle);
  442.             LIST.hScroll = NULL;
  443.             cellSize = LIST.cellSize;
  444.             cellSize.h = width(LIST.rView);
  445.             LCellSize(cellSize, logListHandle);
  446.             horizontalMax = kMaxHorizontalScroll - width((**HSCROLL).contrlRect);
  447.             if (horizontalMax < 0)
  448.                 horizontalMax = 0;
  449.             SetCtlMax(HSCROLL, horizontalMax);
  450.             HiliteControl(
  451.                 HSCROLL,
  452.                 (horizontalMax == 0) ? kDisabledControl : kActiveControl
  453.             );
  454.             viewRect = LIST.rView;
  455.             InsetRect(&viewRect, -1, -1);
  456.             InvalRect(&viewRect);
  457.         }
  458. }
  459.  
  460. /*
  461.  * DoClickInLog: call this when there is a mouse down in the log area (or in
  462.  * one of the scrollbars.
  463.  */
  464. Boolean
  465. DoClickInLog(
  466.         ListHandle                        logListHandle,
  467.         const EventRecord                *eventRecord
  468.     )
  469. {
  470. #define EVENT    (*eventRecord)
  471.  
  472.         Point                            mousePt;
  473.         Boolean                            result;
  474.         Rect                            viewRect;
  475.         short                            part;
  476.         ControlHandle                    theControl;
  477.  
  478.         if (logListHandle == NULL)
  479.             result = FALSE;
  480.         else {
  481.             mousePt = EVENT.where;
  482.             GlobalToLocal(&mousePt);
  483.             /*
  484.              * Handle clicks in the horizontal scrollbar: Do not pass them through
  485.              * LClick, as it does not do what we want and besides, the scrollbar
  486.              * isn't there any more.
  487.              */
  488.             if (PtInRect(mousePt, &(**HSCROLL).contrlRect)) {
  489.                 part = FindControl(mousePt, (**HSCROLL).contrlOwner, &theControl);
  490.                 if (part >= 0 && theControl == HSCROLL) {
  491.                     if (part == inThumb) {
  492.                         if (TrackControl(theControl, mousePt, NULL))
  493.                             ScrollLogList(theControl);
  494.                     }
  495.                     else {
  496.                         TrackControl(theControl, mousePt, (ProcPtr) ScrollLogAction);
  497.                     }
  498.                 }
  499.             }
  500.             else {
  501.                 viewRect = LIST.rView;
  502.                 viewRect.right += kScrollBarOffset;
  503.                 if ((result = PtInRect(mousePt, &viewRect)))
  504.                     (void) LClick(mousePt, EVENT.modifiers, logListHandle);
  505.             }
  506.         }
  507.         return (result);
  508. #undef EVENT
  509. }
  510.  
  511. /*
  512.  * Log errors.
  513.  */
  514. void
  515. LogStatus(
  516.         ListHandle                        logListHandle,
  517.         OSErr                            theError,
  518.         const StringPtr                    infoText
  519.     )
  520. {
  521.         Handle                            macErrorHdl;
  522.         Str255                            msg;
  523.         Str15                            errorValue;
  524.         
  525.         if (logListHandle != NULL && theError != noErr) {
  526.             pstrcpy(msg, infoText);
  527.             pstrcat(msg, "\p: ");
  528.             NumToString(theError, errorValue);
  529.             pstrcat(msg, errorValue);
  530.             macErrorHdl = GetResource('Estr', theError);
  531.             if (macErrorHdl != NULL) {
  532.                 pstrcat(msg, "\p ");
  533.                 pstrcat(msg, (StringPtr) *macErrorHdl);
  534.                 ReleaseResource(macErrorHdl);
  535.             }
  536.             DisplayLogString(logListHandle, msg);
  537.         }
  538. }
  539.  
  540. /*
  541.  * DisplayLogString
  542.  * Call this function to store a string in the list.
  543.  */
  544. void
  545. DisplayLogString(
  546.         ListHandle                        logListHandle,
  547.         const StringPtr                    theString
  548.     )
  549. {
  550.         short                        theRow;
  551.         Boolean                        scrollAtBottom;
  552.         
  553.         if (logListHandle != NULL) {
  554.             /*
  555.              * If there are already logLines in the
  556.              * list, delete the first row of the list.
  557.              * Then, in any case, append this datum at the
  558.              * bottom.
  559.              *
  560.              * The scroll bars are managed as follows:
  561.              * scroll bar is at the bottom, the new datum
  562.              * is selected and autoscrolled into view.
  563.              * Otherwise, the current cell is unchanged.
  564.              */
  565.             theRow = LIST.dataBounds.bottom;
  566.             scrollAtBottom =
  567.                 (GetCtlValue(LIST.vScroll) == GetCtlMax(LIST.vScroll));
  568.             if (theRow >= LOGINFO.logLines)
  569.                 LDelRow(1, 0, logListHandle);
  570.             if (AddStringToList(logListHandle, theString) == noErr) {
  571.                 if (scrollAtBottom)
  572.                     LScroll(0, LIST.dataBounds.bottom - GetCtlValue(LIST.vScroll), logListHandle);
  573.                 if (LOGINFO.logFileRefNum != 0 && LOGINFO.logFileStatus == noErr)
  574.                     WriteLogLine(logListHandle, theString);
  575.             }
  576.         }
  577. failure:
  578.         return;
  579. }
  580.  
  581. static OSErr
  582. AddStringToList(
  583.         ListHandle                        logListHandle,
  584.         ConstStr255Param                theString
  585.     )
  586. {
  587.         StringHandle                    stringHandle;
  588.         Cell                            theCell;
  589.         
  590.         stringHandle = NewString(theString);
  591.         if (stringHandle != NULL) {
  592.             theCell.h = 0;
  593.             theCell.v = LAddRow(1, LIST.dataBounds.bottom, logListHandle);
  594.             if (MemError() == noErr)
  595.                 LSetCell(&stringHandle, sizeof stringHandle, theCell, logListHandle);
  596.         }
  597.         return (MemError());
  598. }
  599.  
  600. /*
  601.  * ScrollLogAction is called by the Toolbox while executing the TrackControl
  602.  * routine.  It has to take care of scrolling the log when the user clicks on the
  603.  * up/down arrow or page parts of the scroll bar.
  604.  */
  605. static pascal void
  606. ScrollLogAction(
  607.         register ControlHandle            theControl,
  608.         short                            partcode
  609.     )
  610. {
  611.         short                            delta;
  612.         
  613.         delta = (width((**theControl).contrlRect) * 7) / 8;
  614.         switch (partcode) {
  615.         case inUpButton:    delta = -CharWidth('M');    break;
  616.         case inPageUp:        delta = -(delta);            break;        
  617.         case inDownButton:    delta = CharWidth('M');        break;
  618.         case inPageDown:    /* All set */                break;
  619.         default:            return;        /* Mouse exited control    */
  620.         }
  621.         SetCtlValue(theControl, GetCtlValue(theControl) + delta);
  622.         ScrollLogList(theControl);
  623. }
  624.  
  625. /*
  626.  * ScrollLogList scrolls the list rectangle in the proper direction and updates
  627.  * the list's horizontal indentation to match.
  628.  */
  629. static void
  630. ScrollLogList(
  631.         ControlHandle                    theControl
  632.     )
  633. {
  634.         ListHandle                        logListHandle;
  635.         short                            delta;
  636.         RgnHandle                        clipRgn;
  637.         RgnHandle                        updateRgn;
  638.         Rect                            viewRect;
  639.         
  640.         logListHandle = (ListHandle) GetCRefCon(theControl);
  641.         /*
  642.          * LIST.indent.h is negative when the cell is scrolled left. Get the
  643.          * amount it's currently scrolled (as a positive value) and set delta
  644.          * to the amount that must be scrolled. Delta will be positive to
  645.          * scroll right (which means that the scrollbar has moved left).
  646.          */
  647.         delta = kZeroIndent - LIST.indent.h - GetCtlValue(theControl);
  648.         if (delta != 0) {
  649.             /*
  650.              * We need to scroll the list cells. Get a clip rectangle so the
  651.              * scrolling is limited to the drawing area, scroll it, and update
  652.              * the stuff that came into view.
  653.              */
  654.             viewRect = LIST.rView;
  655.             clipRgn = NewRgn();
  656.             updateRgn = NewRgn();
  657.             GetClip(clipRgn);
  658.             ClipRect(&viewRect);
  659.             ScrollRect(&viewRect, delta, 0, updateRgn);
  660.             LIST.indent.h += delta;
  661.             LUpdate(updateRgn, logListHandle);
  662.             SetClip(clipRgn);
  663.             DisposeRgn(updateRgn);
  664.             DisposeRgn(clipRgn);
  665.         }
  666. }
  667.         
  668. /*
  669.  * Draw the string stored in the list. The only difference between this function
  670.  * and a "normal" LDEF is that we don't visually indicate selection.
  671.  */
  672. static pascal void
  673. LogListDefProc(
  674.         short                            listMessage,
  675.         Boolean                            listSelect,
  676.         Rect                            *listRect,
  677.         Cell                            listCell,
  678.         short                            listDataOffset,
  679.         short                            listDataLen,
  680.         ListHandle                        logListHandle
  681.     )
  682. {
  683. #pragma unused (listCell)
  684.  
  685.         char                            stringLockState;
  686.         RGBColor                        saveForeColor;
  687.         RGBColor                        saveBackColor;
  688.         StringHandle                    stringHandle;
  689.         
  690.         /*
  691.          * If the userHandle isn't setup, do nothing: this is an initialization
  692.          * or a spurious command while we're disposing of the list.
  693.          */
  694.         if (LIST.userHandle != NULL) {
  695.             TextFont(LOGINFO.fontNumber);
  696.             TextSize(LOGINFO.fontSize);
  697.             if (COLOR_LIST) {
  698.                 GetForeColor(&saveForeColor);
  699.                 GetBackColor(&saveBackColor);
  700.                 RGBForeColor(&LOGINFO.foreColor);
  701.                 RGBBackColor(&LOGINFO.backColor);
  702.             }
  703.             switch (listMessage) {
  704.             case lInitMsg:
  705.                 break;
  706.             case lDrawMsg:
  707.                 EraseRect(listRect);
  708.                 if (listDataLen == sizeof stringHandle) {
  709.                     BlockMove(
  710.                         (*LIST.cells) + listDataOffset,
  711.                         &stringHandle,
  712.                         sizeof stringHandle
  713.                     );
  714.                     /*
  715.                      * We don't indent in the vertical direction: by default,
  716.                      * it contains the font ascent which is fine for DrawText
  717.                      */
  718.                     stringLockState = HGetState((Handle) stringHandle);
  719.                     HLock((Handle) stringHandle);
  720.                     MoveTo(
  721.                         listRect->left + LIST.indent.h,
  722.                         listRect->top + LIST.indent.v
  723.                     );
  724.                     DrawString(*stringHandle);
  725.                     HSetState((Handle) stringHandle, stringLockState);
  726.                 }
  727.                 if (listSelect == FALSE)
  728.                     break;
  729.                 /* Continue to do hilite */
  730.             case lHiliteMsg:
  731. #if 0        /* Hiliting is disabled */
  732. #ifdef THINK_C
  733.                 HiliteMode &= ~(1 << hiliteBit);
  734. #else /* MPW */
  735.                 *((char *) HiliteMode) &= ~(1 << hiliteBit); /* Inside Mac V-61    */
  736. #endif
  737.                 InvertRect(listRect);
  738. #endif
  739.                 break;
  740.             }
  741.             if (COLOR_LIST) {
  742.                 RGBForeColor(&saveForeColor);
  743.                 RGBBackColor(&saveBackColor);
  744.             }
  745.         }
  746. }
  747.  
  748. StringHandle
  749. GetLogStringHandle(
  750.         ListHandle                    logListHandle,
  751.         short                        theRow
  752.     )
  753. {
  754.         Cell                        theCell;
  755.         StringHandle                result;
  756.         short                        dataLength;
  757.         
  758.         dataLength = sizeof result;
  759.         theCell.h = 0;
  760.         theCell.v = theRow;
  761.         LGetCell(&result, &dataLength, theCell, logListHandle);
  762.         if (dataLength != sizeof result)
  763.             result = NULL;
  764.         return (result);
  765. }
  766.  
  767. /*
  768.  * Prompt the user for a file name and write the current log contents
  769.  * to the chosen file. Returns an error status (noErr is ok). This function
  770.  * returns userCanceledErr if the user cancelled the SFPutFile dialog.
  771.  */
  772. OSErr
  773. SaveLogFile(
  774.         ListHandle                        logListHandle,
  775.          ConstStr255Param                dialogPromptString,
  776.         ConstStr255Param                defaultFileName,
  777.         OSType                            creatorType
  778.     )
  779. {
  780.         Point                            where;
  781.         SFReply                            reply;
  782.         DialogTHndl                        dialog;
  783.         Rect                            box;
  784.         OSErr                            status;
  785.         
  786.         /*
  787.          * Center the dialog
  788.          */
  789.         dialog = (DialogTHndl) GetResource('DLOG', putDlgID);
  790.         if (dialog == NULL)                             /* No such dialog!    */
  791.             SetRect(&box, 0, 0, 0, 0);                /* Center on screen    */
  792.         else {
  793.             box = (*dialog)->boundsRect;             /* Dialog shape        */
  794.             ReleaseResource((Handle) dialog);        /* Done with dialog    */
  795.         }
  796.         SetPt(&where,
  797.             (width(qd.thePort->portRect) - width(box)) / 2,
  798.             ((height(qd.thePort->portRect) - GetMBarHeight()) / 3)
  799.                 + GetMBarHeight()
  800.         );
  801.         SFPutFile(where, dialogPromptString, defaultFileName, NULL, &reply);
  802.         if (reply.good == FALSE)
  803.             status = userCanceledErr;
  804.         else {
  805.             SetCursor(*GetCursor(watchCursor));
  806.             status = CreateLogFile(
  807.                         logListHandle,
  808.                         creatorType,
  809.                         reply.fName,
  810.                         reply.vRefNum
  811.                     );
  812.             InitCursor();
  813.             if (status != noErr)
  814.                 (void) FSDelete(reply.fName, reply.vRefNum);
  815.         }
  816.         return (status);
  817. }
  818.  
  819. /*
  820.  * Create a log on the specified file and volume. Normally, called only
  821.  * by SaveLogFile.
  822.  */
  823. OSErr
  824. CreateLogFile(
  825.         ListHandle                        logListHandle,
  826.         OSType                            creatorType,
  827.         ConstStr255Param                fileName,
  828.         short                            vRefNum
  829.     )
  830. {
  831.         OSErr                            status;
  832.         short                            refNum;
  833.         
  834.         /*
  835.          * Create the file, elmininating any duplicate.
  836.          */
  837.         if (creatorType == 0)
  838.             creatorType = 'ttxt';                    /* TeachText            */
  839.         status = Create(fileName, vRefNum, creatorType, 'TEXT');
  840.         if (status == dupFNErr) {                    /* Exists already?        */
  841.             status = FSDelete(fileName, vRefNum);
  842.             if (status == noErr)
  843.                 status = Create(fileName, vRefNum, creatorType, 'TEXT');
  844.         }
  845.         if (status == noErr)
  846.             status = FSOpen(fileName, vRefNum, &refNum);
  847.         if (status == noErr) {
  848.             LOGINFO.logFileRefNum = refNum;
  849.             LOGINFO.logFileVRefNum = vRefNum;
  850.             LOGINFO.logFileStatus = noErr;
  851.             WriteCurrentLog(logListHandle);
  852.             status = LOGINFO.logFileStatus;
  853.         }
  854.         if (status != noErr) {
  855.             (void) CloseLogFile(logListHandle);
  856.             LogStatus(logListHandle, status, "\pCreateLogFile failed");
  857.         }
  858.         return (status);
  859. }
  860.  
  861. /*
  862.  * Write the current contents of the log file. Any error will be
  863.  * in LOGINFO.logFileStatus.
  864.  */
  865. void
  866. WriteCurrentLog(
  867.         ListHandle                        logListHandle
  868.     )
  869. {
  870.         short                            theRow;
  871.         long                            fileLength;
  872.         StringHandle                    stringHandle;
  873.         char                            stringLockState;
  874.  
  875.         
  876.         theRow = LIST.dataBounds.top;
  877.         while (LOGINFO.logFileStatus == noErr && theRow < LIST.dataBounds.bottom) {
  878.             stringHandle = GetLogStringHandle(logListHandle, theRow);
  879.             if (stringHandle != NULL) {
  880.                 stringLockState = HGetState((Handle) stringHandle);
  881.                 HLock((Handle) stringHandle);
  882.                 fileLength = (*stringHandle)[0];
  883.                 LOGINFO.logFileStatus = FSWrite(
  884.                     LOGINFO.logFileRefNum,
  885.                     &fileLength,
  886.                     &(*stringHandle)[1]
  887.                 );
  888.                 HSetState((Handle) stringHandle, stringLockState);
  889.                 if (LOGINFO.logFileStatus == noErr) {
  890.                     fileLength = 1;
  891.                     LOGINFO.logFileStatus = FSWrite(
  892.                                 LOGINFO.logFileRefNum,
  893.                                 &fileLength,
  894.                                 endOfLine
  895.                             );
  896.                 }
  897.             }
  898.             ++theRow;
  899.         };
  900. }        
  901.  
  902. void
  903. WriteLogLine(
  904.         ListHandle                        logListHandle,
  905.         ConstStr255Param                theText
  906.     )
  907. {
  908.         long                            fileLength;
  909.         
  910.         if (LOGINFO.logFileRefNum != 0 && LOGINFO.logFileStatus == noErr) {
  911.             fileLength = theText[0];
  912.             LOGINFO.logFileStatus = FSWrite(
  913.                         LOGINFO.logFileRefNum,
  914.                         &fileLength,
  915.                         &theText[1]
  916.                     );
  917.             if (LOGINFO.logFileStatus == noErr) {
  918.                 fileLength = 1;
  919.                 LOGINFO.logFileStatus = FSWrite(
  920.                             LOGINFO.logFileRefNum,
  921.                             &fileLength,
  922.                             endOfLine
  923.                         );
  924.             }
  925.             if (LOGINFO.logFileStatus != noErr) {
  926.                 CloseLogFile(logListHandle);
  927.                 LogStatus(
  928.                     logListHandle,
  929.                     LOGINFO.logFileStatus,
  930.                     "\pCan't write to log file"
  931.                 );
  932.             }
  933.         }
  934. }
  935.  
  936. OSErr
  937. CloseLogFile(
  938.         ListHandle                        logListHandle
  939.     )
  940. {
  941.         OSErr                            status;
  942.         
  943.         status = noErr;
  944.         if (LOGINFO.logFileRefNum != 0) {
  945.             status = FSClose(LOGINFO.logFileRefNum);
  946.             if (status == noErr)
  947.                 status = FlushVol(NULL, LOGINFO.logFileVRefNum);
  948.         }
  949.         LOGINFO.logFileRefNum = 0;
  950.         LOGINFO.logFileVRefNum = 0;
  951.         if (status != noErr)
  952.             LogStatus(logListHandle, status, "\pCan't close log file");
  953.         if (LOGINFO.logFileStatus == noErr)
  954.             LOGINFO.logFileStatus = status;
  955.         status = LOGINFO.logFileStatus;
  956.         LOGINFO.logFileStatus = noErr;
  957.         return (status);
  958. }
  959.  
  960. OSErr
  961. GetLogFileError(
  962.         ListHandle                        logListHandle
  963.     )
  964. {
  965.         return (LOGINFO.logFileStatus);
  966. }
  967.  
  968. Boolean
  969. HasLogFile(
  970.         ListHandle                        logListHandle
  971.     )
  972. {
  973.         return (LOGINFO.logFileRefNum != 0);
  974. }
  975.  
  976. /*
  977.  * Copyright © 1993, Apple Computer Inc. All Rights reserved.
  978.  *
  979.  * This started life as a generic print driver written by Rich Siegel and posted
  980.  * to Usenet in around 1988 or so. It has been extensively rewritten, but not
  981.  * necessarily improved.
  982.  */
  983.  
  984. /*
  985.  *        OSErr
  986.  *        PrintDriver(
  987.  *                THPrint            hPrint,
  988.  *                void            *clientData,
  989.  *                Boolean            doStyle,
  990.  *                OSErr            (*userPrepProc)(
  991.  *                                        THPrnt        hPrint,
  992.  *                                        void        *clientData
  993.  *                                    ),
  994.  *                OSErr            (*userPageProc)(
  995.  *                                        THPrnt        hPrint,
  996.  *                                        void        *clientData,
  997.  *                                        const Rect    *pageRect,
  998.  *                                        short        pageNum
  999.  *                                    )
  1000.  *            );
  1001.  *        hPrint        A Print Manager handle.  If NULL, ReportPrintDriver
  1002.  *                    will allocate a handle, call the Page Setup dialog,
  1003.  *                    and dispose of the handle on exit.
  1004.  *        clientData    A longword parameter that becomes a parameter to the
  1005.  *                    prepProc and pageProc functions. For the LogManager,
  1006.  *                    it is the ListHandle.
  1007.  *        doStyle        If TRUE, the page setup modal dialog is always called.
  1008.  *                    Otherwise, it's called only if the print record is invalid.
  1009.  *
  1010.  *        userPrepProc A user-provided function that will perform print setup
  1011.  *                    initializations.  It must be defined
  1012.  *                        OSErr            UserPrepProc(
  1013.  *                                THPrint        hPrint,
  1014.  *                                void        *clientData
  1015.  *                            );
  1016.  *                    UserPrepProc returns noErr on success or an error status that
  1017.  *                    will  be returned to the PrintDriver caller on failure.
  1018.  *                    UserPrepProc must set (**hPrint).prJob.iLstPage  to the correct
  1019.  *                    number of pages in the document. If all pages are specified
  1020.  *                    by the user dialog, this field will be 999 when UserPrepProc
  1021.  *                    is called; it must then be re-set to the correct value.
  1022.  *        userPageProc A user-provided function that draws the page.  It is defined:
  1023.  *                        OSErr            UserPageProc(
  1024.  *                                THPrint        hPrint,
  1025.  *                                void        *clientData,
  1026.  *                                Rect        *pageRect,
  1027.  *                                short        pageNum
  1028.  *                        );
  1029.  *                    UserPageProc returns noErr if it completed correctly, or an
  1030.  *                    error code that will be returned to the user.  It should
  1031.  *                    return iPrAbort on user-specified aborts.
  1032.  */
  1033.  
  1034. /*
  1035.  * Note: the serial stuff has not been tested in several years.
  1036.  */
  1037. #ifndef bDevCItoh
  1038. #define bDevCItoh        1                /* ImageWriter                */
  1039. #endif
  1040. #ifndef bDevLaser
  1041. #define bDevLaser        3                /* LaserWriter                */
  1042. #endif
  1043. #define ImageWriter        (bDevCItoh)
  1044. #define    LaserWriter        (bDevLaser)
  1045.  
  1046. OSErr                                PrintLog(
  1047.         ListHandle                        logListHandle,
  1048.         THPrint                            hPrint
  1049.     );
  1050. OSErr                                LogPrepProc(
  1051.         THPrint                            hPrint,
  1052.         void                            *clientData
  1053.     );
  1054. OSErr                                LogPageProc(
  1055.         THPrint                            hPrint,
  1056.         void                            *clientData,
  1057.         const Rect                        *pageRect,
  1058.         short                            pageNumber
  1059.     );
  1060. OSErr                                PrintDriver(
  1061.         THPrint                            hPrint,
  1062.         Boolean                            doStyleDialog,
  1063.         void                            *clientData,
  1064.         OSErr                            (*userPrepProc)(
  1065.                         THPrint        hPrint,
  1066.                         void        *clientData
  1067.                     ),
  1068.         OSErr                            (*userPageProc)(
  1069.                         THPrint        hPrint,
  1070.                         void        *clientData,
  1071.                         const Rect    *pageRect,
  1072.                         short        pageNumber
  1073.                     )
  1074.     );
  1075.  
  1076. /*
  1077.  * PrintLog, LogPrepProc, and LogPageProc are specific to the LogManager.
  1078.  */
  1079.  
  1080. OSErr
  1081. PrintLog(
  1082.         ListHandle                        logListHandle,
  1083.         THPrint                            hPrint
  1084.     )
  1085. {
  1086.         OSErr            status;
  1087.         
  1088.         status = PrintDriver(
  1089.                     hPrint,
  1090.                     FALSE,
  1091.                     logListHandle,
  1092.                     LogPrepProc,
  1093.                     LogPageProc
  1094.                 );
  1095.         return (status);
  1096. }
  1097.  
  1098. OSErr
  1099. LogPrepProc(
  1100.         THPrint                        hPrint,
  1101.         void                        *clientData
  1102.     )
  1103. {
  1104.         unsigned short                linesInLog;
  1105.         unsigned short                linesPerPage;
  1106.         unsigned short                pagesInLog;
  1107.         Rect                        printRect;
  1108. #define logListHandle    ((ListHandle) clientData)
  1109.  
  1110.         /*
  1111.          * We could add a page header here, of course.
  1112.          */
  1113.         printRect = (**hPrint).prInfo.rPage;
  1114.         linesInLog = height(LIST.dataBounds);
  1115.         linesPerPage = height(printRect) / LIST.cellSize.v;
  1116.         pagesInLog = (linesInLog + linesPerPage - 1) / linesPerPage;
  1117.         (**hPrint).prJob.iLstPage = pagesInLog;
  1118.         return (noErr);
  1119. #undef logListHandle
  1120. }
  1121.  
  1122. OSErr
  1123. LogPageProc(
  1124.         THPrint                            hPrint,
  1125.         void                            *clientData,
  1126.         const Rect                        *pageRect,
  1127.         short                            pageNumber
  1128.     )
  1129. {
  1130. #pragma unused (hPrint)
  1131.         unsigned short                    linesPerPage;
  1132.         short                            lastCell;
  1133.         Point                            drawLoc;
  1134.         FontInfo                        info;
  1135.         short                            theRow;
  1136.         StringHandle                    stringHandle;
  1137.         char                            stringLockState;
  1138. #define logListHandle    ((ListHandle) clientData)
  1139.  
  1140.         linesPerPage = height(*pageRect) / LIST.cellSize.v;
  1141.         TextFont(LOGINFO.fontNumber);
  1142.         TextSize(LOGINFO.fontSize);
  1143.         GetFontInfo(&info);
  1144.         theRow = (pageNumber - 1) * linesPerPage + LIST.dataBounds.top;
  1145.         lastCell = theRow + linesPerPage - 1;
  1146.         if (lastCell > LIST.dataBounds.bottom)
  1147.             lastCell = LIST.dataBounds.bottom;
  1148.         SetPt(
  1149.             &drawLoc,
  1150.             LIST.indent.h,
  1151.             pageRect->top + info.ascent
  1152.         );
  1153.         /*
  1154.          * We could add a page header here, of course.
  1155.          */
  1156.         for (; theRow < lastCell; ++theRow) {
  1157.             stringHandle = GetLogStringHandle(logListHandle, theRow);
  1158.             if (stringHandle != NULL) {
  1159.                 stringLockState = HGetState((Handle) stringHandle);
  1160.                 HLock((Handle) stringHandle);
  1161.                 MoveTo(drawLoc.h, drawLoc.v);
  1162.                 DrawString(*stringHandle);
  1163.                 HSetState((Handle) stringHandle, stringLockState);
  1164.             }
  1165.             drawLoc.v += LIST.cellSize.v;
  1166.         };
  1167.         return (PrError());
  1168. }
  1169.  
  1170. /*
  1171.  * Rich Siegel's generic printer driver, somewhat modified.
  1172.  */
  1173. OSErr
  1174. PrintDriver(
  1175.         THPrint                            hPrint,
  1176.         Boolean                            doStyleDialog,
  1177.         void                            *clientData,
  1178.         OSErr                            (*userPrepProc)(
  1179.                         THPrint        hPrint,
  1180.                         void        *clientData
  1181.                     ),
  1182.         OSErr                            (*userPageProc)(
  1183.                         THPrint        hPrint,
  1184.                         void        *clientData,
  1185.                         const Rect    *pageRect,
  1186.                         short        pageNumber
  1187.                     )
  1188.     )
  1189. {
  1190.         OSErr                status;                /* Random status                */
  1191.         Boolean                ourPrintHandle;        /* Did we allocate hPrint        */
  1192.         Boolean                printIsOpen;        /* PROpen-PRClose                 */        
  1193.         Boolean                docIsOpen;            /* PROpenDoc-PRCloseDoc            */
  1194.         Boolean                pageIsOpen;            /* PROpenPage-PRClosePage        */
  1195.         short                nCopies;            /* Number of copies             */
  1196.         short                printDevice;        /* What kind of printer            */
  1197.         Boolean                draftMode;            /* Draft or Spool?                */
  1198.         TPPrPort            printPort;            /* The print port                */
  1199.         TPrStatus            printStatus;        /* PrPicFile status info        */
  1200.         GrafPtr                savePort;            /* Old GrafPort                    */
  1201.         short                iCopy;                /* Which copy                    */
  1202.         short                page;                /* Which page                     */
  1203.         Rect                pageRect;            /* Current page image rect        */
  1204. /*
  1205.  * This macro exits the print handler on any error.
  1206.  */
  1207. #define    CheckError(s)    do {                \
  1208.         if ((status = (s)) != noErr)        \
  1209.             goto exit;                        \
  1210.     } while (0);
  1211.  
  1212.         GetPort(&savePort);
  1213.         status = noErr;
  1214.         printIsOpen = FALSE;
  1215.         docIsOpen = FALSE;
  1216.         pageIsOpen = FALSE;
  1217.         ourPrintHandle = (hPrint == NULL);
  1218.         PrOpen();
  1219.         CheckError(PrError());
  1220.         printIsOpen = TRUE;
  1221.         /*
  1222.          * Allocate the print handle if necessary.  memFullErr on failure
  1223.          * it failed.
  1224.          */
  1225.         if (ourPrintHandle) {
  1226.             hPrint = (THPrint) NewHandle(sizeof (TPrint));
  1227.             if (hPrint == NULL) {
  1228.                 status = MemError();
  1229.                 goto exit;
  1230.             }
  1231.             PrintDefault(hPrint);
  1232.         }
  1233.         /*
  1234.          * Validate the print handle and call the Style Dialog if necessary.  If
  1235.          * the user cancels, just exit (noErr). Then call the job dialog to get
  1236.          * the number of copies. Exit (noErr) if the user Cancels. (Strictly
  1237.          * speaking, we could exit with userCanceledErr, but that just forces
  1238.          * the caller to ignore this informational status.
  1239.          */
  1240.         if (PrValidate(hPrint) || doStyleDialog) {
  1241.             if (PrStlDialog(hPrint) == FALSE)
  1242.                 goto exit;
  1243.         }
  1244.         if (PrJobDialog(hPrint) == FALSE)
  1245.             goto exit;
  1246.         /*
  1247.          * Our setup is done. call the user's prep procedure and exit on errors.
  1248.          */
  1249.         SetCursor(*GetCursor(watchCursor));
  1250.         CheckError((*userPrepProc)(hPrint, clientData));
  1251.         /*
  1252.          * Grab a few interesting parameters:
  1253.          *    printDevice    Which printer we're using
  1254.          *    is_draftMode    TRUE if we're in draft-mode.
  1255.          *    nCopies            We do copies if we're in draft-mode on an Imagewriter.
  1256.          *                    Otherwise, the spooler does it for us. This hasn't
  1257.          *                    been tested in years.
  1258.          */
  1259.         printDevice = ((**hPrint).prStl.wDev >> 8) & 0xFF;
  1260.         draftMode = (**hPrint).prJob.bJDocLoop == bDraftLoop;
  1261.         if (draftMode && printDevice == ImageWriter)
  1262.             nCopies = (**hPrint).prJob.iCopies;
  1263.         else {
  1264.             nCopies = 1;
  1265.         }
  1266.         /*
  1267.          * Printing begins here.  PrOpenDoc sets the port.
  1268.          */
  1269.         printPort = PrOpenDoc(hPrint, NULL, NULL);
  1270.         docIsOpen = TRUE;
  1271.         CheckError(PrError());
  1272.         for (iCopy = 1; iCopy <= nCopies; iCopy++) {
  1273.             for (page = (**hPrint).prJob.iFstPage;
  1274.                         page <= (**hPrint).prJob.iLstPage;
  1275.                         page++) {
  1276.                 SetCursor(*GetCursor(watchCursor));
  1277.                 /*
  1278.                  * Print the current page.
  1279.                  */
  1280.                 PrOpenPage(printPort, NULL);
  1281.                 pageIsOpen = TRUE;
  1282.                 pageRect = (**hPrint).prInfo.rPage;
  1283.                 status = (*userPageProc)(hPrint, clientData, &pageRect, page);
  1284.                 PrClosePage(printPort);
  1285.                 pageIsOpen = FALSE;
  1286.                 CheckError(status);
  1287.             }
  1288.         }
  1289.         SetPort(savePort);
  1290.         PrCloseDoc(printPort);
  1291.         docIsOpen = FALSE;
  1292.         status = PrError();
  1293.         if (draftMode == FALSE && status == noErr) {
  1294.             PrPicFile(hPrint, NULL, NULL, NULL, &printStatus);
  1295.             status = PrError();
  1296.         }
  1297.         /*
  1298.          * Everyone exits here
  1299.          */
  1300. exit:    SetPort(savePort);
  1301.         InitCursor();
  1302.         if (pageIsOpen)
  1303.             PrClosePage(printPort);
  1304.         if (docIsOpen)
  1305.             PrCloseDoc(printPort);
  1306.         if (printIsOpen)
  1307.             PrClose();
  1308.         if (ourPrintHandle && hPrint != NULL)
  1309.             DisposHandle((Handle) hPrint);
  1310.         return (status);
  1311. }
  1312.  
  1313.